/*     */ package com.zimbra.cs.service.mail;
/*     */ 
/*     */ import com.zimbra.common.mailbox.ContactConstants;
/*     */ import com.zimbra.common.mime.ContentType;
/*     */ import com.zimbra.common.mime.MimeDetect;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.soap.Element;
/*     */ import com.zimbra.common.soap.MailConstants;
/*     */ import com.zimbra.common.util.ByteUtil;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.Pair;
/*     */ import com.zimbra.common.util.StringUtil;
/*     */ import com.zimbra.common.util.Version;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.mailbox.Contact;
/*     */ import com.zimbra.cs.mailbox.Contact.Attachment;
/*     */ import com.zimbra.cs.mailbox.ContactGroup;
/*     */ import com.zimbra.cs.mailbox.ContactGroup.Member.Type;
/*     */ import com.zimbra.cs.mailbox.Document;
/*     */ import com.zimbra.cs.mailbox.DocumentDataSource;
/*     */ import com.zimbra.cs.mailbox.MailItem;
/*     */ import com.zimbra.cs.mailbox.MailItem.Type;
/*     */ import com.zimbra.cs.mailbox.MailServiceException;
/*     */ import com.zimbra.cs.mailbox.Mailbox;
/*     */ import com.zimbra.cs.mailbox.MailboxLock;
/*     */ import com.zimbra.cs.mailbox.MailboxManager;
/*     */ import com.zimbra.cs.mailbox.Message;
/*     */ import com.zimbra.cs.mailbox.MessageDataSource;
/*     */ import com.zimbra.cs.mailbox.OperationContext;
/*     */ import com.zimbra.cs.mailbox.util.TagUtil;
/*     */ import com.zimbra.cs.mime.Mime;
/*     */ import com.zimbra.cs.mime.ParsedContact;
/*     */ import com.zimbra.cs.mime.ParsedContact.FieldDelta.Op;
/*     */ import com.zimbra.cs.mime.ParsedContact.FieldDeltaList;
/*     */ import com.zimbra.cs.service.FileUploadServlet;
/*     */ import com.zimbra.cs.service.FileUploadServlet.Upload;
/*     */ import com.zimbra.cs.service.UploadDataSource;
/*     */ import com.zimbra.cs.service.UserServlet;
/*     */ import com.zimbra.cs.service.formatter.VCard;
/*     */ import com.zimbra.cs.service.util.ItemId;
/*     */ import com.zimbra.cs.service.util.ItemIdFormatter;
/*     */ import com.zimbra.soap.DocumentHandler;
/*     */ import com.zimbra.soap.ZimbraSoapContext;
/*     */ import com.zimbra.soap.mail.type.ModifyGroupMemberOperation;
/*     */ import java.io.IOException;
/*     */ import java.util.ArrayList;
/*     */ import java.util.HashMap;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import javax.activation.DataHandler;
/*     */ import javax.activation.DataSource;
/*     */ import javax.mail.MessagingException;
/*     */ import javax.mail.internet.MimePart;
/*     */ import javax.mail.internet.MimePartDataSource;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class CreateContact
/*     */   extends MailDocumentHandler
/*     */ {
/*  77 */   private static final String[] TARGET_FOLDER_PATH = { "cn", "l" };
/*  78 */   private static final String[] RESPONSE_ITEM_PATH = new String[0];
/*  79 */   protected String[] getProxiedIdPath(Element request) { return TARGET_FOLDER_PATH; }
/*  80 */   protected boolean checkMountpointProxy(Element request) { return true; }
/*  81 */   protected String[] getResponseItemPath() { return RESPONSE_ITEM_PATH; }
/*     */   
/*     */ 
/*     */   public Element handle(Element request, Map<String, Object> context)
/*     */     throws ServiceException
/*     */   {
/*  87 */     ZimbraSoapContext zsc = getZimbraSoapContext(context);
/*  88 */     Mailbox mbox = getRequestedMailbox(zsc);
/*  89 */     OperationContext octxt = getOperationContext(zsc, context);
/*  90 */     ItemIdFormatter ifmt = new ItemIdFormatter(zsc);
/*     */     
/*  92 */     boolean verbose = request.getAttributeBool("verbose", true);
/*     */     
/*  94 */     Element cn = request.getElement("cn");
/*  95 */     ItemId iidFolder = new ItemId(cn.getAttribute("l", "7"), zsc);
/*  96 */     String[] tags = TagUtil.parseTags(cn, mbox, octxt);
/*     */     
/*  98 */     Element vcard = cn.getOptionalElement("vcard");
/*     */     List<ParsedContact> pclist;
/*     */     List<ParsedContact> pclist;
/* 101 */     if (vcard != null) {
/* 102 */       pclist = parseAttachedVCard(zsc, octxt, mbox, vcard);
/*     */     } else {
/* 104 */       pclist = new ArrayList(1);
/* 105 */       Pair<Map<String, Object>, List<Contact.Attachment>> cdata = parseContact(cn, zsc, octxt);
/* 106 */       pclist.add(new ParsedContact((Map)cdata.getFirst(), (List)cdata.getSecond()));
/*     */     }
/*     */     
/* 109 */     if (needToMigrateDlist(zsc)) {
/* 110 */       for (ParsedContact pc : pclist) {
/* 111 */         migrateFromDlist(pc);
/*     */       }
/*     */     }
/*     */     
/* 115 */     List<Contact> contacts = createContacts(octxt, mbox, iidFolder, pclist, tags);
/* 116 */     Contact con = null;
/* 117 */     if (contacts.size() > 0) {
/* 118 */       con = (Contact)contacts.get(0);
/*     */     }
/* 120 */     Element response = zsc.createElement(MailConstants.CREATE_CONTACT_RESPONSE);
/* 121 */     if (con != null) {
/* 122 */       if (verbose) {
/* 123 */         ToXML.encodeContact(response, ifmt, octxt, con, true, null);
/*     */       } else
/* 125 */         response.addElement("cn").addAttribute("id", con.getId());
/*     */     }
/* 127 */     return response;
/*     */   }
/*     */   
/*     */   static Pair<Map<String, Object>, List<Contact.Attachment>> parseContact(Element cn, ZimbraSoapContext zsc, OperationContext octxt) throws ServiceException {
/* 131 */     return parseContact(cn, zsc, octxt, null);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   static Pair<Map<String, Object>, List<Contact.Attachment>> parseContact(Element cn, ZimbraSoapContext zsc, OperationContext octxt, Contact existing)
/*     */     throws ServiceException
/*     */   {
/* 140 */     Map<String, Object> fields = new HashMap();
/* 141 */     List<Contact.Attachment> attachments = new ArrayList();
/* 142 */     boolean isContactGroup = false;
/*     */     
/* 144 */     for (Element elt : cn.listElements("a")) {
/* 145 */       String name = elt.getAttribute("n");
/* 146 */       if (name.trim().equals("")) {
/* 147 */         throw ServiceException.INVALID_REQUEST("at least one contact field name is blank", null);
/*     */       }
/*     */       
/*     */ 
/* 151 */       disallowGroupMemberAttr(name);
/*     */       
/* 153 */       Contact.Attachment attach = parseAttachment(elt, name, zsc, octxt, existing);
/* 154 */       if (attach == null) {
/* 155 */         disallowOperation(elt);
/*     */         
/* 157 */         String value = elt.getText();
/* 158 */         StringUtil.addToMultiMap(fields, name, value);
/*     */         
/* 160 */         if ((ContactConstants.A_type.equals(name)) && ("group".equals(value))) {
/* 161 */           isContactGroup = true;
/*     */         }
/*     */       } else {
/* 164 */         attachments.add(attach);
/*     */       }
/*     */     }
/*     */     
/*     */ 
/* 169 */     ContactGroup contactGroup = null;
/* 170 */     for (Element elt : cn.listElements("m")) {
/* 171 */       if (!isContactGroup)
/*     */       {
/* 173 */         throw ServiceException.INVALID_REQUEST("m is only allowed for contact group", null);
/*     */       }
/*     */       
/*     */ 
/* 177 */       disallowOperation(elt);
/*     */       
/* 179 */       if (contactGroup == null) {
/* 180 */         contactGroup = ContactGroup.init(existing, true);
/* 181 */         if (existing != null) {
/* 182 */           contactGroup.removeAllMembers();
/*     */         }
/*     */       }
/* 185 */       String memberType = elt.getAttribute("type");
/* 186 */       String memberValue = elt.getAttribute("value");
/*     */       
/* 188 */       ContactGroup.Member.Type type = ContactGroup.Member.Type.fromSoap(memberType);
/* 189 */       contactGroup.addMember(type, memberValue);
/*     */     }
/* 191 */     if (contactGroup != null) {
/* 192 */       fields.put(ContactConstants.A_groupMember, contactGroup);
/*     */     }
/*     */     
/* 195 */     return new Pair(fields, attachments);
/*     */   }
/*     */   
/*     */   static void disallowOperation(Element elt) throws ServiceException {
/* 199 */     String opStr = elt.getAttribute("op", null);
/* 200 */     if (opStr != null) {
/* 201 */       throw ServiceException.INVALID_REQUEST("op is not allowed", null);
/*     */     }
/*     */   }
/*     */   
/*     */   static void disallowGroupMemberAttr(String attrName) throws ServiceException
/*     */   {
/* 207 */     if (attrName.trim().equals(ContactConstants.A_groupMember)) {
/* 208 */       throw ServiceException.INVALID_REQUEST(ContactConstants.A_groupMember + " cannot be specified as an attribute", null);
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */   static ParsedContact parseContactMergeMode(Element cn, ZimbraSoapContext zsc, OperationContext octxt, Contact existing)
/*     */     throws ServiceException
/*     */   {
/* 216 */     ParsedContact.FieldDeltaList deltaList = new ParsedContact.FieldDeltaList();
/* 217 */     List<Contact.Attachment> attachments = new ArrayList();
/* 218 */     boolean isContactGroup = false;
/*     */     
/* 220 */     for (Element elt : cn.listElements("a")) {
/* 221 */       String name = elt.getAttribute("n");
/* 222 */       if (name.trim().equals("")) {
/* 223 */         throw ServiceException.INVALID_REQUEST("at least one contact field name is blank", null);
/*     */       }
/* 225 */       Contact.Attachment attach = parseAttachment(elt, name, zsc, octxt, existing);
/* 226 */       if (attach == null) {
/* 227 */         String opStr = elt.getAttribute("op", null);
/* 228 */         ParsedContact.FieldDelta.Op op = ParsedContact.FieldDelta.Op.fromString(opStr);
/* 229 */         String value = elt.getText();
/* 230 */         deltaList.addAttrDelta(name, value, op);
/*     */         
/* 232 */         if ((ContactConstants.A_type.equals(name)) && ("group".equals(value)) && (ParsedContact.FieldDelta.Op.REMOVE != op))
/*     */         {
/* 234 */           isContactGroup = true;
/*     */         }
/*     */       } else {
/* 237 */         attachments.add(attach);
/*     */       }
/*     */     }
/*     */     
/* 241 */     boolean discardExistingMembers = false;
/* 242 */     for (Element elt : cn.listElements("m")) {
/* 243 */       if ((!isContactGroup) && (!existing.isGroup())) {
/* 244 */         throw ServiceException.INVALID_REQUEST("m is only allowed for contact group", null);
/*     */       }
/*     */       
/*     */ 
/* 248 */       String opStr = elt.getAttribute("op");
/* 249 */       ModifyGroupMemberOperation groupMemberOp = ModifyGroupMemberOperation.fromString(opStr);
/*     */       
/* 251 */       if (ModifyGroupMemberOperation.RESET.equals(groupMemberOp)) {
/* 252 */         discardExistingMembers = true;
/*     */       } else {
/* 254 */         ParsedContact.FieldDelta.Op op = ParsedContact.FieldDelta.Op.fromString(opStr);
/* 255 */         ContactGroup.Member.Type memberType = ContactGroup.Member.Type.fromSoap(elt.getAttribute("type", null));
/*     */         
/* 257 */         String memberValue = elt.getAttribute("value", null);
/*     */         
/* 259 */         if (memberType == null) {
/* 260 */           throw ServiceException.INVALID_REQUEST("missing member type", null);
/*     */         }
/* 262 */         if (StringUtil.isNullOrEmpty(memberValue)) {
/* 263 */           throw ServiceException.INVALID_REQUEST("missing member value", null);
/*     */         }
/* 265 */         deltaList.addGroupMemberDelta(memberType, memberValue, op);
/*     */       }
/*     */     }
/*     */     
/* 269 */     return new ParsedContact(existing).modify(deltaList, attachments, discardExistingMembers);
/*     */   }
/*     */   
/*     */   private static Contact.Attachment parseAttachment(Element elt, String name, ZimbraSoapContext zsc, OperationContext octxt, Contact existing) throws ServiceException
/*     */   {
/* 274 */     String attachId = elt.getAttribute("aid", null);
/* 275 */     if (attachId != null) {
/* 276 */       FileUploadServlet.Upload up = FileUploadServlet.fetchUpload(zsc.getAuthtokenAccountId(), attachId, zsc.getAuthToken());
/* 277 */       UploadDataSource uds = new UploadDataSource(up);
/* 278 */       return new Contact.Attachment(new DataHandler(uds), name, (int)up.getSize());
/*     */     }
/*     */     
/* 281 */     int itemId = (int)elt.getAttributeLong("id", -1L);
/* 282 */     String part = elt.getAttribute("part", null);
/* 283 */     if ((itemId != -1) || ((part != null) && (existing != null))) {
/* 284 */       MailItem item = itemId == -1 ? existing : getRequestedMailbox(zsc).getItemById(octxt, itemId, MailItem.Type.UNKNOWN);
/*     */       try
/*     */       {
/* 287 */         if ((item instanceof Contact)) {
/* 288 */           Contact contact = (Contact)item;
/* 289 */           if ((part != null) && (!part.equals(""))) {
/*     */             try {
/* 291 */               int partNum = Integer.parseInt(part) - 1;
/* 292 */               if ((partNum >= 0) && (partNum < contact.getAttachments().size())) {
/* 293 */                 Contact.Attachment att = (Contact.Attachment)contact.getAttachments().get(partNum);
/* 294 */                 return new Contact.Attachment(att.getDataHandler(), name, att.getSize());
/*     */               }
/*     */             } catch (NumberFormatException nfe) {}
/* 297 */             throw ServiceException.INVALID_REQUEST("invalid contact part number: " + part, null);
/*     */           }
/* 299 */           VCard vcf = VCard.formatContact(contact);
/* 300 */           return new Contact.Attachment(vcf.getFormatted().getBytes("utf-8"), "text/x-vcard; charset=utf-8", name, vcf.fn + ".vcf");
/*     */         }
/* 302 */         if ((item instanceof Message)) {
/* 303 */           Message msg = (Message)item;
/* 304 */           if ((part != null) && (!part.equals(""))) {
/*     */             try {
/* 306 */               MimePart mp = Mime.getMimePart(msg.getMimeMessage(), part);
/* 307 */               if (mp == null) {
/* 308 */                 throw MailServiceException.NO_SUCH_PART(part);
/*     */               }
/* 310 */               DataSource ds = new MimePartDataSource(mp);
/* 311 */               return new Contact.Attachment(new DataHandler(ds), name);
/*     */             } catch (MessagingException me) {
/* 313 */               throw ServiceException.FAILURE("error parsing blob", me);
/*     */             }
/*     */           }
/* 316 */           DataSource ds = new MessageDataSource(msg);
/* 317 */           return new Contact.Attachment(new DataHandler(ds), name, (int)msg.getSize());
/*     */         }
/* 319 */         if ((item instanceof Document)) {
/* 320 */           Document doc = (Document)item;
/* 321 */           if ((part != null) && (!part.equals(""))) {
/* 322 */             throw MailServiceException.NO_SUCH_PART(part);
/*     */           }
/* 324 */           DataSource ds = new DocumentDataSource(doc);
/* 325 */           return new Contact.Attachment(new DataHandler(ds), name, (int)doc.getSize());
/*     */         }
/*     */       } catch (IOException ioe) {
/* 328 */         throw ServiceException.FAILURE("error attaching existing item data", ioe);
/*     */       } catch (MessagingException e) {
/* 330 */         throw ServiceException.FAILURE("error attaching existing item data", e);
/*     */       }
/*     */     }
/*     */     
/* 334 */     return null;
/*     */   }
/*     */   
/*     */   private static List<ParsedContact> parseAttachedVCard(ZimbraSoapContext zsc, OperationContext octxt, Mailbox mbox, Element vcard) throws ServiceException
/*     */   {
/* 339 */     String text = null;
/* 340 */     String messageId = vcard.getAttribute("mid", null);
/* 341 */     String attachId = vcard.getAttribute("aid", null);
/*     */     
/* 343 */     if (attachId != null)
/*     */     {
/* 345 */       FileUploadServlet.Upload up = FileUploadServlet.fetchUpload(zsc.getAuthtokenAccountId(), attachId, zsc.getAuthToken());
/*     */       try {
/* 347 */         text = new String(ByteUtil.getContent(up.getInputStream(), 0));
/*     */       } catch (IOException e) {
/* 349 */         throw ServiceException.FAILURE("error reading vCard", e);
/*     */       }
/* 351 */     } else if (messageId == null)
/*     */     {
/* 353 */       text = vcard.getText();
/*     */     }
/*     */     else {
/* 356 */       ItemId iid = new ItemId(messageId, zsc);
/* 357 */       String part = vcard.getAttribute("part");
/* 358 */       String[] acceptableTypes = { "text/plain", "text/vcard", "text/x-vcard", "text/directory" };
/* 359 */       String charsetWanted = mbox.getAccount().getAttr("zimbraPrefMailDefaultCharset", null);
/* 360 */       text = fetchItemPart(zsc, octxt, mbox, iid, part, acceptableTypes, charsetWanted);
/*     */     }
/*     */     
/* 363 */     List<VCard> cards = VCard.parseVCard(text);
/* 364 */     if ((cards == null) || (cards.size() == 0))
/* 365 */       throw MailServiceException.UNABLE_TO_IMPORT_CONTACTS("no vCards present in attachment", null);
/* 366 */     List<ParsedContact> pclist = new ArrayList(cards.size());
/* 367 */     for (VCard vcf : cards)
/* 368 */       pclist.add(vcf.asParsedContact());
/* 369 */     return pclist;
/*     */   }
/*     */   
/*     */   public static List<Contact> createContacts(OperationContext oc, Mailbox mbox, ItemId iidFolder, List<ParsedContact> list, String[] tags)
/*     */     throws ServiceException
/*     */   {
/* 375 */     List<Contact> toRet = new ArrayList();
/*     */     
/* 377 */     mbox.lock.lock();
/*     */     try {
/* 379 */       for (ParsedContact pc : list) {
/* 380 */         toRet.add(mbox.createContact(oc, pc, iidFolder.getId(), tags));
/*     */       }
/*     */     } finally {
/* 383 */       mbox.lock.release();
/*     */     }
/* 385 */     return toRet;
/*     */   }
/*     */   
/*     */ 
/*     */   static String fetchItemPart(ZimbraSoapContext zsc, OperationContext octxt, Mailbox mbox, ItemId iid, String part, String[] acceptableMimeTypes, String charsetWanted)
/*     */     throws ServiceException
/*     */   {
/* 392 */     String text = null;
/*     */     try {
/* 394 */       if (iid.isLocal())
/*     */       {
/* 396 */         if (!mbox.getAccountId().equals(iid.getAccountId())) {
/* 397 */           mbox = MailboxManager.getInstance().getMailboxByAccountId(iid.getAccountId());
/*     */         }
/* 399 */         Message msg = mbox.getMessageById(octxt, iid.getId());
/* 400 */         MimePart mp = Mime.getMimePart(msg.getMimeMessage(), part);
/* 401 */         String ctype = new ContentType(mp.getContentType()).getContentType();
/* 402 */         String fname = mp.getFileName();
/* 403 */         if ((fname != null) && (("application/octet-stream".equals(ctype)) || ("application/ms-tnef".equals(ctype)))) {
/* 404 */           String guess = MimeDetect.getMimeDetect().detect(fname);
/* 405 */           if (guess != null) {
/* 406 */             ctype = guess;
/*     */           }
/*     */         }
/*     */         boolean typeAcceptable;
/* 410 */         if (acceptableMimeTypes != null) {
/* 411 */           boolean typeAcceptable = false;
/* 412 */           for (String type : acceptableMimeTypes) {
/* 413 */             if ((type != null) && (type.equalsIgnoreCase(ctype))) {
/* 414 */               typeAcceptable = true;
/* 415 */               break;
/*     */             }
/*     */           }
/*     */         } else {
/* 419 */           typeAcceptable = true;
/*     */         }
/* 421 */         if (!typeAcceptable)
/* 422 */           throw MailServiceException.INVALID_CONTENT_TYPE(ctype);
/* 423 */         text = Mime.getStringContent(mp, charsetWanted);
/*     */       }
/*     */       else {
/* 426 */         Map<String, String> params = new HashMap();
/* 427 */         params.put("part", part);
/* 428 */         byte[] content = UserServlet.getRemoteContent(zsc.getAuthToken(), iid, params);
/* 429 */         text = new String(content, "utf-8");
/*     */       }
/*     */     } catch (IOException e) {
/* 432 */       throw ServiceException.FAILURE("error fetching message part: iid=" + iid + ", part=" + part, e);
/*     */     } catch (MessagingException e) {
/* 434 */       throw ServiceException.FAILURE("error fetching message part: iid=" + iid + ", part=" + part, e);
/*     */     }
/* 436 */     return text;
/*     */   }
/*     */   
/*     */   static boolean needToMigrateDlist(ZimbraSoapContext zsc) throws ServiceException {
/* 440 */     String ua = zsc.getUserAgent();
/*     */     
/*     */ 
/* 443 */     if ("Zimbra Systems Client".equals(ua)) {
/* 444 */       return true;
/*     */     }
/* 446 */     Pair<String, Version> connectorVersion = DocumentHandler.zimbraConnectorClientVersion(zsc);
/* 447 */     if (connectorVersion != null)
/*     */     {
/* 449 */       if ("ZimbraMigration".equals(connectorVersion.getFirst())) {
/* 450 */         Version newContactGroupAPISupported = new Version("9.0.0");
/* 451 */         if (((Version)connectorVersion.getSecond()).compareTo(newContactGroupAPISupported) < 0) {
/* 452 */           return true;
/*     */         }
/*     */       }
/*     */       else {
/* 456 */         Version newContactGroupAPISupported = new Version("8.0.0");
/* 457 */         if (((Version)connectorVersion.getSecond()).compareTo(newContactGroupAPISupported) < 0) {
/* 458 */           return true;
/*     */         }
/*     */       }
/*     */     }
/* 462 */     return false;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   private static final String DEFAULT_FOLDER = "7";
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   static void migrateFromDlist(ParsedContact pc)
/*     */     throws ServiceException
/*     */   {
/* 477 */     Map<String, String> fields = pc.getFields();
/* 478 */     String dlist = (String)fields.get(ContactConstants.A_dlist);
/* 479 */     if (dlist != null) {
/*     */       try {
/* 481 */         ContactGroup contactGroup = ContactGroup.init();
/* 482 */         contactGroup.migrateFromDlist(dlist);
/* 483 */         fields.put(ContactConstants.A_groupMember, contactGroup.encode());
/* 484 */         fields.remove(ContactConstants.A_dlist);
/*     */       } catch (Exception e) {
/* 486 */         ZimbraLog.contact.info("skipped migrating contact group, dlist=[%s]", dlist, e);
/*     */       }
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/service/mail/CreateContact.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */